Hello World Demo 分析
MainWindow.xaml
1 | <Window x:Class="Simplest.MainWindow" |
MainWindow.xaml.cs
1 | namespace Simplest |
在Plot中插入图表1
2
3
4
5
6
7
8
9
10
11
12
13public static LineGraph AddLineGraph(this Plotter2D plotter, IPointDataSource pointSource, Pen linePen, Description description)
{
LineGraph graph = new LineGraph
{
DataSource = pointSource,
LinePen = linePen
};
graph.Filters.Add(new InclinationFilter());
graph.Filters.Add(new FrequencyFilter());
//graph.Filters.Add(new CountFilter());
plotter.Children.Add(graph);
return graph;
}
LineGraph的继承关系1
2
3
4
5
6
7
8
9
10System.Object
System.Windows.Threading.DispatcherObject
System.Windows.DependencyObject
System.Windows.Media.Visual
System.Windows.UIElement
System.Windows.FrameworkElement
Microsoft.Research.DynamicDataDisplay.PlotterElement
Microsoft.Research.DynamicDataDisplay.ViewportElement2D
Microsoft.Research.DynamicDataDisplay.PointsGraphBase
Microsoft.Research.DynamicDataDisplay.LineGraph
ChartPlotter的继承关系1
2
3
4
5
6
7
8
9
10
11System.Object
System.Windows.Threading.DispatcherObject
System.Windows.DependencyObject
System.Windows.Media.Visual
System.Windows.UIElement
System.Windows.FrameworkElement
System.Windows.Controls.Control
System.Windows.Controls.ContentControl
Microsoft.Research.DynamicDataDisplay.Plotter
Microsoft.Research.DynamicDataDisplay.Plotter2D
Microsoft.Research.DynamicDataDisplay.ChartPlotter
LineGraph如何绘制
1 | filters.CollectionChanged += filters_CollectionChanged; |
调用graph.Filters.Add(new InclinationFilter())之后, 触发filters_CollectionChanged事件1
2
3
4
5void filters_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
filteredPoints = null;
Update();
}
Update定义在父类ViewportElement2D中1
2
3
4
5
6
7
8
9
10
11
12
13
14protected void Update()
{
if (Viewport == null) return;
UpdateCore();
if (!beforeFirstUpdate)
{
updateCalled = true;
InvalidateVisual();
}
beforeFirstUpdate = false;
}
protected virtual void UpdateCore() { }
LineGraph重写UpdateCore1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27private FakePointList filteredPoints;
protected override void UpdateCore()
{
if (DataSource == null) return;
Rect output = Viewport.Output;
var transform = GetTransform();
if (filteredPoints == null || !(transform.DataTransform is IdentityTransform))
{
IEnumerable<Point> points = GetPoints();
ContentBounds = BoundsHelper.GetViewportBounds(points, transform.DataTransform);
transform = GetTransform();
List<Point> transformedPoints = transform.DataToScreen(points);
// Analysis and filtering of unnecessary points
filteredPoints = new FakePointList(FilterPoints(transformedPoints),
output.Left, output.Right);
Offset = new Vector();
}
else
{...
}
}
UpdateCore先是将数据转换到窗口, 然后在调用Filter对数据进行过滤(裁剪)
ViewportElement2D是重点,有Update,UpdateCore(纯虚函数),OnRender(重写),OnRenderCore(纯虚函数). OnRender原本是UIElement的纯虚函数, ViewportElement2D重写OnRender1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70private bool shouldReRender = true;
private DrawingGroup graphContents;
protected sealed override void OnRender(DrawingContext drawingContext)
{
if (Viewport == null) return;
Rect output = Viewport.Output;
if (output.Width == 0 || output.Height == 0) return;
if (output.IsEmpty) return;
if (Visibility != Visibility.Visible) return;
if (shouldReRender || manualTranslate || renderTarget == RenderTo.Image || beforeFirstUpdate || updateCalled)
{
if (graphContents == null)
{
graphContents = new DrawingGroup();
}
if (beforeFirstUpdate)
{
Update();
}
using (DrawingContext context = graphContents.Open())
{
if (renderTarget == RenderTo.Screen)
{
RenderState state = CreateRenderState(Viewport.Visible, RenderTo.Screen);
OnRenderCore(context, state);
}
else
{
// for future use
}
}
updateCalled = false;
}
// thumbnail is not created, if
// 1) CreateThumbnail is false
// 2) this graph has IsLayer property, set to false
if (ShouldCreateThumbnail)
{
RenderThumbnail();
}
if (!manualClip)
{
drawingContext.PushClip(new RectangleGeometry(output));
}
bool translate = !manualTranslate && IsTranslated;
if (translate)
{
drawingContext.PushTransform(new TranslateTransform(offset.X, offset.Y));
}
drawingContext.DrawDrawing(graphContents);
if (translate)
{
drawingContext.Pop();
}
if (!manualClip)
{
drawingContext.Pop();
}
shouldReRender = true;
}
protected abstract void OnRenderCore(DrawingContext dc, RenderState state);
LineGraph重写OnRenderCore1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29protected override void OnRenderCore(DrawingContext dc, RenderState state)
{
if (DataSource == null) return;
if (filteredPoints.HasPoints)
{
StreamGeometry geometry = new StreamGeometry();
using (StreamGeometryContext context = geometry.Open())
{
context.BeginFigure(filteredPoints.StartPoint, false, false);
context.PolyLineTo(filteredPoints, true, true);
}
geometry.Freeze();
const Brush brush = null;
Pen pen = LinePen;
bool isTranslated = IsTranslated;
if (isTranslated)
{
dc.PushTransform(new TranslateTransform(Offset.X, Offset.Y));
}
dc.DrawGeometry(brush, pen, geometry);
if (isTranslated)
{
dc.Pop();
}
}
}
参考:
UIElement.OnRender Method (DrawingContext)
https://msdn.microsoft.com/en-us/library/system.windows.uielement.onrender(v=vs.110).aspx
DrawingContext Class
https://msdn.microsoft.com/en-us/library/system.windows.media.drawingcontext(v=vs.110).aspx
public abstract class DrawingContext : DispatcherObject, IDisposable
Describes visual content using draw, push, and pop commands.
Use a DrawingContext to populate a Visual or a Drawing with visual content.
Although the DrawingContext draw methods appear similar to the draw methods of the System.Drawing.Graphics type, they function very differently:DrawingContext is used with a retained mode graphics system, while the System.Drawing.Graphics type is used with an immediate mode graphics system. When you use a DrawingContext object’s draw commands, you are actually storing a set of rendering instructions (although the exact storage mechanism depends on the type of object that supplies the DrawingContext) that will later be used by the graphics system; you are not drawing to the screen in real-time. For more information about how the Windows Presentation Foundation (WPF) graphics system works, see WPF Graphics Rendering Overview.
You never directly instantiate a DrawingContext; you can, however, acquire a drawing context from certain methods, such as DrawingGroup.Openand DrawingVisual.RenderOpen.
StreamGeometry Class (比Shape更轻量)
https://msdn.microsoft.com/en-us/library/system.windows.media.streamgeometry(v=vs.110).aspx)
Defines a geometric shape, described using a StreamGeometryContext. This geometry is light-weight alternative to PathGeometry: it does not support data binding, animation, or modification.
Optimizing Performance: 2D Graphics and Imaging
https://msdn.microsoft.com/en-us/library/bb613591(v=vs.110).aspx
Since Shape objects derive from the FrameworkElement class, using them can add significantly more memory consumption in your application. If you really do not need the FrameworkElement features for your graphical content, consider using the lighter-weight Drawing objects.
How to: Create a Shape Using a StreamGeometry
https://msdn.microsoft.com/en-us/library/ms742199(v=vs.110).aspx